/*
 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "se_napi_oma_reader.h"

#include "loghelper.h"
#include "se_napi_oma_common.h"
#include "se_napi_oma_session.h"

namespace OHOS {
namespace SE {
namespace OMAPI {

thread_local napi_ref NapiSEOmaReader::consRef_ = nullptr;

napi_value JSOmaReaderClassConstructor(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function JSOmaReaderClassConstructor");
    napi_value thisVar = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
    NapiSEOmaReader* omaJSClass = new NapiSEOmaReader();

    napi_status status = napi_wrap(
        env,
        thisVar,
        omaJSClass,
        [](napi_env env, void *data, void *hint) {
            NapiSEOmaReader *service = (NapiSEOmaReader *)data;
            if (service) {
                delete service;
                service = nullptr;
            }
        },
        nullptr,
        nullptr);
    if (status != napi_ok) {
        if (omaJSClass != nullptr) {
            delete omaJSClass;
            omaJSClass = nullptr;
        }
        ErrorLog("napi_wrap failed, object is null. status = %{public}d", status);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    return thisVar;
}
napi_status NapiSEOmaReader::CheckNapiReaderStatus(napi_env env, NapiSEOmaReader *napiReader)
{
    if (napiReader == nullptr || napiReader->reader_ == nullptr) {
        ErrorLog("GetService napiReader nullptr!");
        return napi_invalid_arg;
    }
    return napi_ok;
}
void NapiSEOmaReader::DefineSEOmaReaderJsClass(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("getName", NapiSEOmaReader::GetName),
        DECLARE_NAPI_FUNCTION("isSecureElementPresent", NapiSEOmaReader::IsSecureElementPresent),
        DECLARE_NAPI_FUNCTION("openSession", NapiSEOmaReader::OpenSession),
        DECLARE_NAPI_FUNCTION("closeSessions", NapiSEOmaReader::CloseSessions),
    };
    // define JS class SEOmaReader, JS_Constructor is the callback function
    napi_value constructor = nullptr;
    napi_define_class(env, "SEOmaReader", NAPI_AUTO_LENGTH, JSOmaReaderClassConstructor,
        nullptr, sizeof(desc)/sizeof(desc[0]), desc, &constructor);
    napi_create_reference(env, constructor, 1, &consRef_);
}

napi_value NapiSEOmaReader::GetName(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaReader::GetName:");
    napi_value result = nullptr;
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    NapiSEOmaReader *napiReader = nullptr;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiReader));
    napi_status status = CheckNapiReaderStatus(env, napiReader);
    if (status != napi_ok) {
        ErrorLog("NapiSEOmaReader::GetName CheckNapiReaderStatus failed");
        NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, SE_ILLEGAL_STATE_ERROR, "GetName");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // get name by call native method
    std::string name = "";
    int statusCode = napiReader->reader_->GetName(name);
    if (statusCode != ErrorCode::SE_SUCCESS) {
        ErrorLog("GetName failed, statusCode = %{public}d", statusCode);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &result);
    DebugLog("Reader::GetName: %{public}s", name.c_str());
    return result;
}

napi_value NapiSEOmaReader::IsSecureElementPresent(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaReader::IsSecureElementPresent:");
    napi_value result = nullptr;
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    bool isPresent = false;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    // unwrap from thisVar to retrieve the native instance
    NapiSEOmaReader *napiReader = nullptr;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiReader));
    napi_status status = CheckNapiReaderStatus(env, napiReader);
    if (status != napi_ok) {
        ErrorLog("NapiSEOmaReader::IsSecureElementPresent CheckNapiReaderStatus failed");
        NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, SE_ILLEGAL_STATE_ERROR, "IsSecureElementPresent");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // get IsSecureElementPresent by calling native method
    int statusCode = napiReader->reader_->IsSecureElementPresent(isPresent);
    NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, statusCode, "IsSecureElementPresent");
    napi_get_boolean(env, isPresent, &result);
    DebugLog("Reader::IsSecureElementPresent: %{public}d", isPresent);
    return result;
}

napi_value NapiSEOmaReader::OpenSession(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaReader::OpenSession:");
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    // unwrap from thisVar to retrieve the native instance
    NapiSEOmaReader *napiReader = nullptr;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiReader));
    napi_status status = CheckNapiReaderStatus(env, napiReader);
    if (status != napi_ok) {
        NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, SE_ILLEGAL_STATE_ERROR, "OpenSession");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // Open session by call native method, throw exception when fails
    napi_value session = nullptr;
    NapiSEOmaSession *napiSession = new NapiSEOmaSession();
    int statusCode = napiReader->reader_->OpenSession(napiSession->session_);
    napiSession->reader_ = napiReader;
    if (napiSession->session_ == nullptr) {
        delete napiSession;
        napiSession = nullptr;
        NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, SE_ILLEGAL_STATE_ERROR, "OpenSession");
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, statusCode, "OpenSession");

    // new instance of JS object
    napi_value constructor = nullptr;
    NAPI_CALL(env, napi_get_reference_value(env, NapiSEOmaSession::consRef_, &constructor));
    NAPI_CALL(env, napi_new_instance(env, constructor, argc, argv, &session));
    status = napi_wrap(
        env, session, napiSession,
        [](napi_env env, void *data, void *hint) {
            ErrorLog("NapiSEOmaReader::OpenSession delete JsSession!");
            if (data) {
                NapiSEOmaSession* service = (NapiSEOmaSession*)data;
                if (service) {
                    delete service;
                    service = nullptr;
                }
            }
        }, nullptr, nullptr);
    if (status != napi_ok) {
        if (napiSession != nullptr) {
            delete napiSession;
            napiSession = nullptr;
        }
        ErrorLog("OpenSession, napi_wrap failed, object is null. status = %{public}d", status);
        NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, SE_ILLEGAL_STATE_ERROR, "OpenSession");
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    DebugLog("Reader::OpenSession success");
    return session;
}

napi_value NapiSEOmaReader::CloseSessions(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaReader::CloseSessions:");
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    // unwrap from thisVar to retrieve the native instance
    NapiSEOmaReader *napiReader = nullptr;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiReader));
    napi_status status = CheckNapiReaderStatus(env, napiReader);
    if (status != napi_ok) {
        ErrorLog("NapiSEOmaReader::OpenSession CheckNapiReaderStatus failed");
        NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, SE_ILLEGAL_STATE_ERROR, "CloseSessions");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // shutdown service by call native method, throw exception when fails
    int statusCode = napiReader->reader_->CloseSessions();
    NapiSEOmaCommon::CheckSEStatusCodeAndThrow(env, statusCode, "CloseSessions");
    DebugLog("Reader::CloseSessions success");
    return NapiSEOmaCommon::CreateUndefined(env);
}
} // namespace KITS
}  // namespace SE
}  // namespace OHOS
